/* Copyrights Security Engineering Research Group 
 * Author: Nauman (http://recluze.wordpress.com) 
 * 
 */

package edu.serg.mbar.ucon.pdp;

import javax.swing.event.EventListenerList;
import java.util.Vector;

import org.apache.log4j.Logger;

import edu.serg.mbar.ucon.UpdateType;
import edu.serg.mbar.ucon.PolicyType;
import edu.serg.mbar.ucon.interfaces.DecisionEvent;
import edu.serg.mbar.ucon.interfaces.IsMutabilityCapable;
import edu.serg.mbar.ucon.interfaces.IsUCONCompatible;
import edu.serg.mbar.ucon.interfaces.MutabilityEvent;
import edu.serg.mbar.ucon.interfaces.DecisionEvent.DecisionType;
import edu.serg.mbar.ucon.interfaces.PolicyEvaluator;
import edu.serg.mbar.ucon.interfaces.PrimitiveActionsHandler;

/**
 * This class stores subject information including the subject attributes and an
 * {@link ObjectCollection} object. It doesn't store a subject id because it is
 * used as the value for key "subject id".
 * 
 * 
 * @author Nauman (recluze.wordpress.com)
 * @author Sanaullah
 * 
 */
public class AccessManager {
	/* The log for this class */
	private static Logger log = Logger.getLogger(AccessManager.class);;

	/** Decision event handlers */
	private EventListenerList decisionEventListeners = new EventListenerList();

	/** Mutability event handlers */
	private EventListenerList mutabilityEventListeners = new EventListenerList();
	
//	/** Attribute Readers */
//	private AttributeReader attributeReader ;
	
	/** Policy Evaluator */
	private PolicyEvaluator plcEvaluator; 
	
	/** Primitive Actions Handler */
	private PrimitiveActionsHandler prmActionsHandler;
	
	public AccessManager(PolicyEvaluator plcEval, PrimitiveActionsHandler prmActHandler)
	{
		plcEvaluator = plcEval;
//		attributeReader = atRdr;
		prmActionsHandler = prmActHandler;
	}
	
	private AccessManager()
	{
		
	}

	/**
	 * Register a DecisionEvent listener.
	 * 
	 * @param listener
	 *            The listener object to be added
	 */
	public void addDecisionEventListener(IsUCONCompatible listener) {
		log.debug("Adding a new DecisionEvent listener.");
		decisionEventListeners.add(IsUCONCompatible.class, listener);
		
	}

	/**
	 * Register a Mutability Event listener.
	 * 
	 * @param listener
	 *            The listener object to be added
	 */
	public void addMutabilityEventListener(IsMutabilityCapable listener) {
		log.debug("Adding a new MutabilityEvent listener.");
		mutabilityEventListeners.add(IsMutabilityCapable.class, listener);
	}
	
//	/**
//	 * Register a Attribute reader
//	 * 
//	 * @param attribReader
//	 * 		The reader object to be added
//	 */
//	public void assignAttributeReader(AttributeReader attribReader){
//		log.debug ("assigning an to accessmanager attribute Reader");
//		attributeReader= attribReader;
//		
//	}

	/**
	 * Unregister a DecisionEvent listener.
	 * 
	 * @param listener
	 *            The listener object to be removed
	 */
	public void removeDecisionEventListener(IsUCONCompatible listener) {
		log.debug("Removing a DecisionEvent listener.");
		decisionEventListeners.remove(IsUCONCompatible.class, listener);
	}

	/**
	 * Unregister a MutabilityEvent listener.
	 * 
	 * @param listener
	 *            The listener object to be removed
	 */
	public void removeMutabilityEventListener(IsMutabilityCapable listener) {
		log.debug("Removing a MutabilityEvent listener.");
		mutabilityEventListeners.remove(IsMutabilityCapable.class, listener);
	}
	
	/**
	 * Request to access an object or excercise a right on the object is processed by this Function. this function also performs PreUpdates for the requesting state. 
	 * 
	 * @param subjectID
	 * 		The ID of the Subject which is requesting to excercise a right on a requested Object
	 * @param ObjectID
	 * 		The Object the subject is requesting
	 * @param right
	 * 		The right that the subject wants to excercise on the Object
	 * @return
	 * 		returns true or false based on whether access to the object is allowed
	 */
	public boolean requestObject(String subjectID, String objectID, String right){
		boolean grantAccess = false ;
//		if (plcEvaluator==null){
//			plcEvaluator=new PolicyEvaluator(attributeReader);
//		}
		try
		{
			prmActionsHandler.handlePrimitiveActions(subjectID, objectID, right, "Requesting");
		}
		catch(Exception e)
		{
			log.error("An error occured while performing Preupdates on request for Object");
			e.printStackTrace();
		}
		log.info("Request from subjectID["+subjectID +"] for Object whose ID is " + objectID +" for the Right of "+ right);
		grantAccess = plcEvaluator.evaluatePolicy(subjectID, objectID, right, "PermitAccess");
		log.info("Access Decision = " + grantAccess  );
		return grantAccess;
	}

//	/**
//	 * The function performs PreUpdates whenever a request for an object is received.   
//	 * 
//	 * @param subjectID
//	 * 	The ID of the subject which is requesting for a right on an object.
//	 * @param objectID
//	 * 	The ID of the the Object which is being requested by the subject 
//	 * @param right
//	 * 	The right that the subject has requested
//	 * 
//	 * @return
//	 */
//	
//	private boolean preUpdatesOnRequest (String subjectID, String objectID, String right)
//	{
//		boolean updatesPerformed =false;
//		prmActionsHandler.handlePrimitiveActions(subjectID, objectID, right, "PreUpdatesOnRequest");
//		
//		return updatesPerformed;
//	}
	
	/**
	 * Fires the decision event. The DecisionEvent object must be constructed
	 * beforehand and passed to this method
	 * 
	 * @param dEvent
	 *            the event to fire
	 * @throws AccessManagerConfigurationException
	 */
	protected void fireDecisionEvent(DecisionEvent dEvent)
			throws AccessManagerConfigurationException {
		log.debug("Firing decision event of type: " + dEvent.decisionType);
		Object[] listeners = decisionEventListeners.getListenerList();
		int num = listeners.length;
		if (num < 1) {
			log.error("No continuity decision processors registered.");
			throw new AccessManagerConfigurationException(
					"No continuity decision processors registered.");

		}

		for (int i = 0; i < num; i += 2) {
			if (listeners[i] == IsUCONCompatible.class) {
				DecisionEventDispatcher d = new DecisionEventDispatcher(
						((IsUCONCompatible) listeners[i + 1]), dEvent);
				d.start();
			}
		}
	}

	/**
	 * Fires the mutability event. The MutabilityEvent object must be
	 * constructed beforehand and passed to this method
	 * 
	 * @param mEvent
	 *            the event to fire
	 * @throws AccessManagerConfigurationException
	 */
	protected void fireMutabilityEvent(MutabilityEvent mEvent)
			throws AccessManagerConfigurationException {
		log.debug("Firing mutability event for [ " + mEvent.target + " , "
				+ mEvent.attribute + "]");
		Object[] listeners = mutabilityEventListeners.getListenerList();
		int num = listeners.length;
		if (num < 1) {
			log.error("No mutability event processors registered.");
			throw new AccessManagerConfigurationException(
					"No mutability event processors registered.");

		}

		for (int i = 0; i < num; i += 2) {
			if (listeners[i] == IsMutabilityCapable.class) {
				MutabilityEventDispatcher d = new MutabilityEventDispatcher(
						((IsMutabilityCapable) listeners[i + 1]), mEvent);

				d.start();
			}
		}
	}

	// A Simple test function to fire an event

	public void test() throws AccessManagerConfigurationException {
		for (int i = 0; i < 10; i++) {
			DecisionEvent d = new DecisionEvent(this);
			d.decisionType = DecisionType.PERMIT;
			fireDecisionEvent(d);
			// log.debug("-----test message");

			MutabilityEvent m = new MutabilityEvent(this, "subject", "attrib",
					null,UpdateType.PREUPDATE);
			
			fireMutabilityEvent(m);
			// log.debug("--- test message for mutability firing");
		}
	}

	/**
	 * A nested class for dispatching decision events. Each event has to be
	 * dispatched by a new thread so that if the PEP fails to respond correctly,
	 * the AccessManager does not suffer
	 * 
	 * @author Nauman
	 * 
	 */
	class DecisionEventDispatcher extends Thread {
		IsUCONCompatible cl;
		DecisionEvent de;

		public DecisionEventDispatcher(IsUCONCompatible c, DecisionEvent d) {
			cl = c;
			de = d;
		}

		public void run() {
			cl.processContinuityDecision(de);
		}
	}

	/**
	 * A nested class for dispatching mutability events. Each event has to be
	 * dispatched by a new thread so that if the PEP fails to respond correctly,
	 * the AccessManager does not suffer
	 * 
	 * @author Nauman
	 * 
	 */
	class MutabilityEventDispatcher extends Thread {
		IsMutabilityCapable cl;
		MutabilityEvent de;

		public MutabilityEventDispatcher(IsMutabilityCapable c,
				MutabilityEvent d) {
			cl = c;
			de = d;
		}

		public void run() {
			cl.processAttributeMutability(de);
		}
	}

}
